
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

/**
 * AUTO-GENERATED FILE. DO NOT MODIFY.
 */

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
import * as echarts from '../../core/echarts.js';
import { createHashMap, each, hasOwn, keys, map } from 'zrender/lib/core/util.js';
import { isCartesian2DSeries, findAxisModels } from './cartesianAxisHelper.js';
import { getDataDimensionsOnAxis, unionAxisExtentFromData } from '../axisHelper.js';
import { ensureScaleRawExtentInfo } from '../scaleRawExtentInfo.js'; // A tricky: the priority is just after dataZoom processor.
// If dataZoom has fixed the min/max, this processor do not need to work.
// TODO: SELF REGISTERED.

echarts.registerProcessor(echarts.PRIORITY.PROCESSOR.FILTER + 10, {
	getTargetSeries: function (ecModel) {
		var seriesModelMap = createHashMap();
		ecModel.eachSeries(function (seriesModel) {
			isCartesian2DSeries(seriesModel) && seriesModelMap.set(seriesModel.uid, seriesModel);
		});
		return seriesModelMap;
	},
	overallReset: function (ecModel, api) {
		var seriesRecords = [];
		var axisRecordMap = createHashMap();
		prepareDataExtentOnAxis(ecModel, axisRecordMap, seriesRecords);
		calculateFilteredExtent(axisRecordMap, seriesRecords);
		shrinkAxisExtent(axisRecordMap);
	}
});

function prepareDataExtentOnAxis(ecModel, axisRecordMap, seriesRecords) {
	ecModel.eachSeries(function (seriesModel) {
		if (!isCartesian2DSeries(seriesModel)) {
			return;
		}

		var axesModelMap = findAxisModels(seriesModel);
		var xAxisModel = axesModelMap.xAxisModel;
		var yAxisModel = axesModelMap.yAxisModel;
		var xAxis = xAxisModel.axis;
		var yAxis = yAxisModel.axis;
		var xRawExtentInfo = xAxis.scale.rawExtentInfo;
		var yRawExtentInfo = yAxis.scale.rawExtentInfo;
		var data = seriesModel.getData(); // If either axis controlled by other filter like "dataZoom",
		// use the rule of dataZoom rather than adopting the rules here.

		if (xRawExtentInfo && xRawExtentInfo.frozen || yRawExtentInfo && yRawExtentInfo.frozen) {
			return;
		}

		seriesRecords.push({
			seriesModel: seriesModel,
			xAxisModel: xAxisModel,
			yAxisModel: yAxisModel
		}); // FIXME: this logic needs to be consistent with
		// `coord/cartesian/Grid.ts#_updateScale`.
		// It's not good to implement one logic in multiple places.

		unionAxisExtentFromData(prepareAxisRecord(axisRecordMap, xAxisModel).condExtent, data, xAxis.dim);
		unionAxisExtentFromData(prepareAxisRecord(axisRecordMap, yAxisModel).condExtent, data, yAxis.dim);
	});
}

function calculateFilteredExtent(axisRecordMap, seriesRecords) {
	each(seriesRecords, function (seriesRecord) {
		var xAxisModel = seriesRecord.xAxisModel;
		var yAxisModel = seriesRecord.yAxisModel;
		var xAxis = xAxisModel.axis;
		var yAxis = yAxisModel.axis;
		var xAxisRecord = prepareAxisRecord(axisRecordMap, xAxisModel);
		var yAxisRecord = prepareAxisRecord(axisRecordMap, yAxisModel);
		xAxisRecord.rawExtentInfo = ensureScaleRawExtentInfo(xAxis.scale, xAxisModel, xAxisRecord.condExtent);
		yAxisRecord.rawExtentInfo = ensureScaleRawExtentInfo(yAxis.scale, yAxisModel, yAxisRecord.condExtent);
		xAxisRecord.rawExtentResult = xAxisRecord.rawExtentInfo.calculate();
		yAxisRecord.rawExtentResult = yAxisRecord.rawExtentInfo.calculate(); // If the "xAxis" is set `min`/`max`, some data items might be out of the cartesian.
		// then the "yAxis" may needs to calculate extent only based on the data items inside
		// the cartesian (similar to what "dataZoom" did).
		// A typical case is bar-racing, where bars ara sort dynamically and may only need to
		// displayed part of the whole bars.

		var data = seriesRecord.seriesModel.getData(); // For duplication removal.

		var condDimMap = {};
		var tarDimMap = {};
		var condAxis;
		var tarAxisRecord;

		function addCondition(axis, axisRecord) {
			// But for simplicity and safety and performance, we only adopt this
			// feature on category axis at present.
			var condExtent = axisRecord.condExtent;
			var rawExtentResult = axisRecord.rawExtentResult;

			if (axis.type === 'category' && (condExtent[0] < rawExtentResult.min || rawExtentResult.max < condExtent[1])) {
				each(getDataDimensionsOnAxis(data, axis.dim), function (dataDim) {
					if (!hasOwn(condDimMap, dataDim)) {
						condDimMap[dataDim] = true;
						condAxis = axis;
					}
				});
			}
		}

		function addTarget(axis, axisRecord) {
			var rawExtentResult = axisRecord.rawExtentResult;

			if (axis.type !== 'category' && (!rawExtentResult.minFixed || !rawExtentResult.maxFixed)) {
				each(getDataDimensionsOnAxis(data, axis.dim), function (dataDim) {
					if (!hasOwn(condDimMap, dataDim) && !hasOwn(tarDimMap, dataDim)) {
						tarDimMap[dataDim] = true;
						tarAxisRecord = axisRecord;
					}
				});
			}
		}

		addCondition(xAxis, xAxisRecord);
		addCondition(yAxis, yAxisRecord);
		addTarget(xAxis, xAxisRecord);
		addTarget(yAxis, yAxisRecord);
		var condDims = keys(condDimMap);
		var tarDims = keys(tarDimMap);
		var tarDimExtents = map(tarDims, function () {
			return initExtent();
		});
		var condDimsLen = condDims.length;
		var tarDimsLen = tarDims.length;

		if (!condDimsLen || !tarDimsLen) {
			return;
		}

		var singleCondDim = condDimsLen === 1 ? condDims[0] : null;
		var singleTarDim = tarDimsLen === 1 ? tarDims[0] : null;
		var dataLen = data.count(); // Time consuming, because this is a "block task".
		// Simple optimization for the vast majority of cases.

		if (singleCondDim && singleTarDim) {
			for (var dataIdx = 0; dataIdx < dataLen; dataIdx++) {
				var condVal = data.get(singleCondDim, dataIdx);

				if (condAxis.scale.isInExtentRange(condVal)) {
					unionExtent(tarDimExtents[0], data.get(singleTarDim, dataIdx));
				}
			}
		} else {
			for (var dataIdx = 0; dataIdx < dataLen; dataIdx++) {
				for (var j = 0; j < condDimsLen; j++) {
					var condVal = data.get(condDims[j], dataIdx);

					if (condAxis.scale.isInExtentRange(condVal)) {
						for (var k = 0; k < tarDimsLen; k++) {
							unionExtent(tarDimExtents[k], data.get(tarDims[k], dataIdx));
						} // Any one dim is in range means satisfied.

						break;
					}
				}
			}
		}

		each(tarDimExtents, function (tarDimExtent, i) {
			var dim = tarDims[i]; // FIXME: if there has been approximateExtent set?

			data.setApproximateExtent(tarDimExtent, dim);
			var tarAxisExtent = tarAxisRecord.tarExtent = tarAxisRecord.tarExtent || initExtent();
			unionExtent(tarAxisExtent, tarDimExtent[0]);
			unionExtent(tarAxisExtent, tarDimExtent[1]);
		});
	});
}

function shrinkAxisExtent(axisRecordMap) {
	axisRecordMap.each(function (axisRecord) {
		var tarAxisExtent = axisRecord.tarExtent;

		if (tarAxisExtent) {
			var rawExtentResult = axisRecord.rawExtentResult;
			var rawExtentInfo = axisRecord.rawExtentInfo; // Shink the original extent.

			if (!rawExtentResult.minFixed && tarAxisExtent[0] > rawExtentResult.min) {
				rawExtentInfo.modifyDataMinMax('min', tarAxisExtent[0]);
			}

			if (!rawExtentResult.maxFixed && tarAxisExtent[1] < rawExtentResult.max) {
				rawExtentInfo.modifyDataMinMax('max', tarAxisExtent[1]);
			}
		}
	});
}

function prepareAxisRecord(axisRecordMap, axisModel) {
	return axisRecordMap.get(axisModel.uid) || axisRecordMap.set(axisModel.uid, {
		condExtent: initExtent()
	});
}

function initExtent() {
	return [Infinity, -Infinity];
}

function unionExtent(extent, val) {
	val < extent[0] && (extent[0] = val);
	val > extent[1] && (extent[1] = val);
}